package org.balthie.demo.jdk.lang.reflect.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

/**
 * @author：balthie@126.com
 * @createtime ： 2022年8月26日 下午4:15:30
 * @description 处理定制模型 和 北向网关模型转换
 * @since version 初始于版本 1.1.1
 */
public class ReqAdapterTransformUtil
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ReqAdapterTransformUtil.class);
    
    private static final ConcurrentHashMap<Class, Field[]> classFieldsCache = new ConcurrentHashMap<>(10);
    private static final ConcurrentHashMap<Class, Method[]> classMethodsCache = new ConcurrentHashMap<>(10);
    
    /**
     * @author：balthie@126.com
     * @createtime ： 2022年8月30日 上午10:28:14
     * @description 将业务定制类型参数（如，ExpressCompanyDo，StorageCompanyDo）转换为 DDD OHS 统一PLRequest
     * 
     * @since version 初始于版本 1.1.1
     * @param adapter  参考 com.viomi.info.client.facade.utils.mock.ExpressCompanyDoMock
     * @return 
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <T extends IPLRequest> T trans2PLReq(IRequestAdapter<T> adapter)
            throws InstantiationException, IllegalAccessException
    {
        // OHS/PL 实际IPLRequest类型
        Class IPLRequestClazz = adapter.getAdapteeClass();
        
        // 将 业务定制参数实现类中 与 IPLRequestClazz 对应的属性 设置到 IPLRequestClazz对象中
        T newInstance = (T) autoFill(adapter, IPLRequestClazz);
        
        // 将业务定制类自定义属性，转为 IPLRequest的 List<???>
        List<Object> attrs = getItemAttrBaseBOList(adapter, IPLRequestClazz);
// 待确认如何传 expressNumValidRuler (物流单号校验规则), bizCompanyId（业务公司ID） 等业务定制参数
        
        return newInstance;
    }
    
    /**
     * @author：balthie@126.com
     * @createtime ： 2022年8月29日 下午6:23:41
     * @description 将ICreateCampanyReqAdapter 实现类中的业务定制属性，转换成 北向网关 Request业务自定义参数
     * @since version 初始于版本 1.1.1
     * @param bean 业务定制类
     * @param transType 转换的目标 Req类型
     * @return
     */
    public static List<Object> getItemAttrBaseBOList(IRequestAdapter bean, Class transType)
    {
        List<Object> result = new ArrayList();
        // 业务定制类的 成员变量
        Field[] businessFields = getDeclaredFieldsThroughCache(bean.getClass());
        
        // DDD北向网关 Request 成员变量
        Field[] requestFields = getDeclaredFieldsThroughCache(transType);
        
        for(Field field : businessFields)
        {
            // 业务定制类 重复定义的 成员变量 不转换
            if(isDuplicatedField(requestFields, field)) continue;
            
            field.setAccessible(true);
            if(Modifier.isStatic(field.getModifiers())) continue;
            
            try
            {
                // 如果属性有被赋值，且在置空集合中，不会被置空，而是被修改
                if(!ObjectUtils.isEmpty(field.get(bean)))
                {
                    if(field.getType().equals(Date.class))
                    {
                        Date dataValue = (Date) field.get(bean);
//                        ItemAttrBaseBO bo = ItemAttrBaseBO.builder().code(field.getName()).value(dataValue.getTime()).build();
//                        result.add(bo);
                    }
                    else
                    {
//                        ItemAttrBaseBO bo = ItemAttrBaseBO.builder().code(field.getName()).value(field.get(bean)).build();
//                        result.add(bo);
                    }
                }
                else
                {
                    // 未发现有使用此参数的场景，猜测用于软删除attr value用
//                    if(CollectionUtils.isNotEmpty(setToNullCodes) && setToNullCodes.contains(field.getName()))
//                    {
//                    ItemAttrBaseBO bo = ItemAttrBaseBO.builder().code(field.getName()).value(null).build();
//                    result.add(bo);
//                    }
                }
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }
        }
        return result;
    }
    
    private static Boolean isDuplicatedField(Field[] requestFields, Field field)
    {
        for(Field requestField : requestFields)
        {
            if(requestField.getName().equals(field.getName()))
            {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }
    
    private static Field[] getDeclaredFieldsThroughCache(Class clazz)
    {
        Field[] cachedFields = classFieldsCache.get(clazz);
        if(cachedFields == null)
        {
            cachedFields = clazz.getDeclaredFields();
            classFieldsCache.put(clazz, cachedFields);
        }
        return cachedFields;
    }
    
    private static Method[] getDeclaredMethodsThroughCache(Class clazz)
    {
        Method[] cachedMethods = classMethodsCache.get(clazz);
        if(cachedMethods == null)
        {
            cachedMethods = clazz.getDeclaredMethods();
            classMethodsCache.put(clazz, cachedMethods);
        }
        return cachedMethods;
    }
    
    /**
     * @author：balthie@126.com
     * @createtime ： 2022年8月30日 下午6:13:04
     * @description 自动将 adapter业务定制类 的属性值，填充到IPLRequest对象
     *              <p>
     *              通过遍历 IPLRequest 中的 setXXX属性器， 寻找 adapter业务定制类对应的 getXXX属性器
     *              <b>IRequestAdapter 声明的getXXX属性器 必须与 IPLRequest保持一致</b>
     * @since version 初始于版本 1.1.1
     * @param adapter
     * @param IPLRequestClazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static Object autoFill(IRequestAdapter adapter, Class IPLRequestClazz)
            throws InstantiationException, IllegalAccessException
    {
        Object newInstance = IPLRequestClazz.newInstance();
        Method[] declaredMethods = getDeclaredMethodsThroughCache(IPLRequestClazz);
        String getterName = null;
        Method getter = null;
        for(Method m : declaredMethods)
        {
            if(m.getName().startsWith("set"))
            {
                try
                {
                    getterName = m.getName().replaceFirst("set", "get");
                    getter = adapter.getClass().getMethod(getterName);
                    m.invoke(newInstance, getter.invoke(adapter, null));
                }
                catch (Exception e)
                {
                    LOGGER.error(getterName + "  " + e.getMessage());
                }
            }
        }
        return newInstance;
    }
}
